var FM_DXFConversion = Class.create();
FM_DXFConversion.prototype = {
    initialize: function () {
        this.utils = new FM_svgUtils();
        this.databaseUtils = new FM_autocadDatabase_utils();
    },

    type: 'FM_DXFConversion',

    //does all the operations and uses all the layers from the DXF
    convertAndSave: function (versionSysId, attachmentSysId, isNewFloor, layers) {
		
        if (!this.load(attachmentSysId))
            throw new Error('Failed to load attachment');

        var validation = this.parseLayers(layers);
        this.renderFromLayers();
		
		if (!layers){
			if (!this.databaseUtils.generateFacilities(versionSysId, this.roomList, isNewFloor, this.building, this.floor)){
				throw new Error('A floor with this ID already exists in ServiceNow');
			}	
		}
        this.databaseUtils.saveSVG(versionSysId, this.SVG, this.bgGeoJSON);
        this.databaseUtils.saveGeoJSON(versionSysId, this.geoJSONList);
        return validation;
    },

    //Loads the DXF string and set the property DXF_String
    load: function (attachmentSysId) {

        //sets the DXF_String property
        var gr = new GlideRecord("sys_attachment");
        if (gr.get(attachmentSysId)) {
            this.DXF_String = new GlideSysAttachment().getContent(gr);
            return true;
        }
        return false;
    },

    //builds the svg html string from a list o selected layers, uses all layers from the class if no arrays is passed
    renderFromLayers: function (selectedPathsArray) {
        //sets the SVG string as an HTML entity
        this.SVG = "";

        if (typeof selectedPathsArray === 'undefined') {
            selectedPathsArray = this.layersList;
        }

        var pixelToMillimeterConversionRatio = 3.543299873306695;

        if (selectedPathsArray.length > 0) {
            var minX = 9007199254740990;//Max safe integer
            var minY = 9007199254740990;//Max safe integer
            var maxX = -9007199254740990;//Min safe integer
            var maxY = -9007199254740990;//Min safe integer

            // Select layers to draw from layers list and add its paths to svg
            for (var i = 0; i < selectedPathsArray.length; i++) {

                //get the min max valuies for x and y regarding the svg as a whole
                if (minX > selectedPathsArray[i].minX) {
                    minX = selectedPathsArray[i].minX;
                }
                if (minY > selectedPathsArray[i].minY) {
                    minY = selectedPathsArray[i].minY;
                }
                if (maxX < selectedPathsArray[i].maxX) {
                    maxX = selectedPathsArray[i].maxX;
                }
                if (maxY < selectedPathsArray[i].maxY) {
                    maxY = selectedPathsArray[i].maxY;
                }

                this.SVG += selectedPathsArray[i].paths;
            }

            //compare the distance from min x to max x, watch out for negative numbers
            var width = minX > 0 || (minX < 0 && maxX < 0) ? Math.abs(maxX - minX) : Math.abs(minX) + maxX;
            //compare the distance from min x to max x, watch out for negative numbers
            var height = minY > 0 || (minY < 0 && maxY < 0) ? Math.abs(maxY - minY) : Math.abs(minY) + maxY;


            this.SVG = this.utils.format('<svg viewBox="{0} {1} {2} {3}" transform="scale(1,-1)" version="1.1" xmlns="http://www.w3.org/2000/svg">\n', minX, minY, width, height) +
                '<g style="stroke:black; stroke-width:0.1; stroke-linecap:round; stroke-linejoin:round; fill:none">\n' +
                this.SVG +
                '</g>\n' +
                '</svg>\n';
        }
    },

    // SVG generation
    dxfObjectToSvgSnippet: function (layer, dxfObject, utils) {

        //detects the max and min values for x and y for that layer
        function setLayerMinMax(x1, y1, x2, y2) {
            if (x1 < layer.minX) {
                layer.minX = x1;
            }
            if (y1 < layer.minY) {
                layer.minY = y1;
            }
            if (x2 > layer.maxX) {
                layer.maxX = x2;
            }
            if (y2 > layer.maxY) {
                layer.maxY = y2;
            }
        }

        function getLineSvg(x1, y1, x2, y2) {
            setLayerMinMax(x1, y1, x2, y2);
            layer.paths += utils.format('<path d="M{0},{1} {2},{3}"/>\n', x1, y1, x2, y2);
        }

        function deg2rad(deg) {
            return deg * (Math.PI / 180);
        }

        switch (dxfObject.type) {
            case 'LINE':
                getLineSvg(dxfObject.x, dxfObject.y, dxfObject.x1, dxfObject.y1);
                break;
            case 'CIRCLE':
                setLayerMinMax(dxfObject.x - dxfObject.r, dxfObject.y - dxfObject.r, dxfObject.x + dxfObject.r, dxfObject.y + dxfObject.r);
                layer.paths += utils.format('<circle cx="{0}" cy="{1}" r="{2}"/>\n', dxfObject.x, dxfObject.y, dxfObject.r);
                break;
            case 'ARC':
                var x1 = dxfObject.x + dxfObject.r * Math.cos(deg2rad(dxfObject.a0));
                var y1 = dxfObject.y + dxfObject.r * Math.sin(deg2rad(dxfObject.a0));
                var x2 = dxfObject.x + dxfObject.r * Math.cos(deg2rad(dxfObject.a1));
                var y2 = dxfObject.y + dxfObject.r * Math.sin(deg2rad(dxfObject.a1));

                if (dxfObject.a1 < dxfObject.a0) {
                    dxfObject.a1 += 360;
                }
                var largeArcFlag = dxfObject.a1 - dxfObject.a0 > 180 ? 1 : 0;

                setLayerMinMax(x1, y1, x2, y2);
                layer.paths += utils.format('<path d="M{0},{1} A{2},{3} 0 {4},1 {5},{6}"/>\n',
                    x1, y1, dxfObject.r, dxfObject.r, largeArcFlag, x2, y2);
                break;
            case 'LWPOLYLINE':

                var vertices = dxfObject.vertices;
                for (var i = 0; i < vertices.length - 1; i++) {
                    var vertice1 = vertices[i];
                    var vertice2 = vertices[i + 1];
                    getLineSvg(vertice1.x, vertice1.y, vertice2.x, vertice2.y);
                }
                break;
        }
        return layer;
    },

    // geoJSON generation
    dxfToGeoJSON: function (dxfObject, uniqueReference, roomList) {

        var coordinatesArr = [];
        var typeOf = '';
        var uniqueReferenceArr = uniqueReference.split(',');

        // Store the building and floor of the first entity with these populated fields
        if (this.facilityFlag && typeof uniqueReferenceArr[3] != 'undefined' && typeof uniqueReferenceArr[4] != 'undefined') {
            this.building = uniqueReferenceArr[3];
            this.floor = uniqueReferenceArr[4];
            this.facilityFlag = false;
        }

        //Add the id's of each room to an array
        if (typeof roomList != 'undefined')
            roomList.push(uniqueReferenceArr[5]);

        switch (dxfObject.type) {

            case 'LINE':
                coordinatesArr.push([dxfObject.x, dxfObject.y], [dxfObject.x1, dxfObject.y1]);
                typeOf = 'Line';
                break;

            case 'LWPOLYLINE':
                for (var i = 0; i < dxfObject.vertices.length; i++) {
                    coordinatesArr.push([dxfObject.vertices[i].x, dxfObject.vertices[i].y]);

                    //The last point is the same as the first
                    if (i === dxfObject.vertices.length - 1) {
                        coordinatesArr.push([dxfObject.vertices[0].x, dxfObject.vertices[0].y]);
                    }

                }
                typeOf = 'Polygon';
                break;
        }
        return {
            properties: {
                building: uniqueReferenceArr[3],
                floor: uniqueReferenceArr[4],
                id: uniqueReferenceArr[5],
            },
            type: 'Feature',
            geometry: {
                type: typeOf,
                coordinates: [coordinatesArr]

            }
        };
    },

    //reads the DXF as string and parses it as an array of layers
    parseLayers: function (manualLayer) {

        //sets layers property as an array of { name: <string>, paths: <string> }
        //returns an array of { name: <string>, paths: <string> }
        var dxfString = this.DXF_String;
        var utils = this.utils;

        var groupCodes = {
            0: 'entityType',
            2: 'name',
            8: 'layerName',
            10: 'x',
            11: 'x1',
            20: 'y',
            21: 'y1',
            40: 'r', // Radius
            50: 'a0', // Arc start angle
            51: 'a1',// Arc end angle
            1001: 'regApp',
            1070: 'layerCode' // Layer data
        };

        var supportedEntities = [
            'LINE',
            'CIRCLE',
            'ARC',
            'LWPOLYLINE'
        ];

        var counter = 0;
        var code = null;
        var isLayerSectionActive = false;
        var isEntitiesSectionActive = false;
        var isXDataSectionActive = false;
        var currentLayer = null;
        var currentRegApp = null;
        var uniqueReference = '';
        var layers = [];
        var roomList = [];
        var object = {};
        this.facilityFlag = true;
		var geoJSONLayers = null;
		var SVGLayers = null;
		
		if (!manualLayer){
			geoJSONLayers = this.databaseUtils.getGeoJSONLayerConfiguration();
			SVGLayers = this.databaseUtils.getSVGLayerConfiguration();
		} else {
			SVGLayers = manualLayer;
		}

        var bgGeoJSON = {
            type: 'FeatureCollection',
            features: []
        };
        var geoJSON = {
            type: 'FeatureCollection',
            features: []
        };

        // Normalize platform-specific newlines.
        dxfString = dxfString.replace(/\r\n/g, '\n');
        dxfString = dxfString.replace(/\r/g, '\n');

        var lineCount = 0;
		var objectContext = this;
        dxfString.split('\n').forEach(function (line) {
            lineCount++;

            line = line.trim();

            if (counter++ % 2 === 0) {
                code = parseInt(line);

            } else {
                var value = line;
                var groupCode = groupCodes[code];

                //Start looking for layers to store
                if (groupCode === 'name' && value === 'LAYER') {
                    isLayerSectionActive = true;
                }

                // Store create and store new layers from DXF layer table
                if (isLayerSectionActive) {
                    if (groupCode === 'name' &&
                        value !== 'LAYER' &&
                        value !== 'DEFPOINTS' &&
                        value !== 'Defpoints') { // So it can skip table name and defpoints layer

                        var newLayer = {};
                        newLayer.name = value;
                        newLayer.isVisible = true;
                        newLayer.paths = '';
                        newLayer.minX = 9007199254740990;
                        newLayer.minY = 9007199254740990;
                        newLayer.maxX = -9007199254740990;
                        newLayer.maxY = -9007199254740990;
                        newLayer.paths = '';

                        layers.push(newLayer);
                    }

                    if (groupCode === 'entityType' && value === 'ENDTAB') {
                        isLayerSectionActive = false;
                    }
                }

                //Start looking for drawable entities
                if (groupCode === 'name' && value === 'ENTITIES') {
                    isEntitiesSectionActive = true;
                }

                if (groupCode === 'layerName') {
                    currentLayer = value;
                }

                if (isEntitiesSectionActive) {

                    // Store the current registered application name
                    if (groupCode === 'regApp') {
                        currentRegApp = value;
                    }

                    // Check if it is in the XData section of the AFM10 application (Archibus naming)
                    if (groupCode === 'layerCode' && currentRegApp === 'AFM10' /* || currentRegApp === 'OurApplicationName' */) {
                        isXDataSectionActive = true;
                    }

                    // Parsing XData information (building, floor, roomID, etc)
                    if (isXDataSectionActive) {
                        uniqueReference += value + ',';
                    }

                    if (groupCode === 'entityType') {  // New entity starts.

                        if (object.type) {
                            object.layerName = currentLayer;

							
							if(geoJSONLayers && geoJSONLayers.length > 0){
                            // GeoJSON layer selection and conversion
								geoJSONLayers.forEach(function (layer) {
									if (currentLayer === layer) {
										geoJSON.features.push(objectContext.dxfToGeoJSON(object, uniqueReference, roomList));
									}
								});
							}

							if(SVGLayers && SVGLayers.length > 0){
								
								// SVG layer selection and conversion
								SVGLayers.forEach(function (SVGConfiguration) {
									if (currentLayer === SVGConfiguration) {
										bgGeoJSON.features.push(objectContext.dxfToGeoJSON(object, uniqueReference));

										for (var i = 0; i < layers.length; i++) {
											//TODO: Test if this condition is needed
											if (layers[i].name === currentLayer)
												layers[i] = objectContext.dxfObjectToSvgSnippet(layers[i], object, utils);
										}
									}
								});
							}
                        }

                        isXDataSectionActive = false;
                        uniqueReference = '';
                        object = supportedEntities.indexOf(value) > -1 ? { type: value } : {};

                        if (value === 'ENDSEC') {
                            isEntitiesSectionActive = false;
                        }

                    } else if (object.type && typeof groupCode !== 'undefined') {  // Known entity property recognized.
                        object[groupCode] = parseFloat(value);

                        if (object.type == 'LWPOLYLINE' && groupCode === 'y') {
                            if (!object.vertices) {
                                object.vertices = [];
                            }
                            object.vertices.push({ x: object.x, y: object.y });

                        }
                    }
                }
            }
        });
        this.roomList = roomList;
        this.layersList = layers;
        this.geoJSONList = geoJSON;
        this.bgGeoJSON = bgGeoJSON;
        return layers.length > 0;
    },

};